package com.operation.net.service.impl;

import com.operation.net.dto.SnmpMessageDTO;
import com.operation.net.service.MibDataAcqService;
import com.operation.net.vo.MibVO;
import org.snmp4j.*;
import org.snmp4j.event.ResponseEvent;
import org.snmp4j.mp.MPv1;
import org.snmp4j.mp.MPv2c;
import org.snmp4j.mp.MPv3;
import org.snmp4j.mp.SnmpConstants;
import org.snmp4j.security.*;
import org.snmp4j.smi.*;
import org.snmp4j.transport.DefaultUdpTransportMapping;
import org.snmp4j.util.DefaultPDUFactory;
import org.snmp4j.util.TreeEvent;
import org.snmp4j.util.TreeUtils;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;

@Service
public class MibDataAcqServiceImpl implements MibDataAcqService {


    @Override
    public  List<MibVO> showMib(SnmpMessageDTO info) {

        List<MibVO> mibMessage = null;
        try {
            mibMessage = doWalk(info);

        } catch (IOException e) {
            e.printStackTrace();

        }

        return mibMessage;
    }


    public  List<MibVO> setMib(SnmpMessageDTO info) {
        List<MibVO> mibMessage = null;
        try {
            mibMessage = doSet(info);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return mibMessage;
    }

    public static List<MibVO> doWalk(SnmpMessageDTO info) throws IOException {


        List<MibVO> list =new ArrayList<>();

        //创建目标对象
        CommunityTarget target =null;
        target = createTarget(info);

        //初始化Snmp
        Snmp snmp=initSnmp(info);

        //TreeUtils遍历子树
        TreeUtils treeUtils = new TreeUtils(snmp, new DefaultPDUFactory());
        List<TreeEvent> events = treeUtils.getSubtree(target, new OID(info.getOid()));
        if (events == null || events.size() == 0) {
            System.out.println("Error: Unable to read table...");
        }

        assert events != null;
        for (TreeEvent event : events) {
            if (event == null) {
                continue;
            }
            if (event.isError()) {
                System.out.println("Error: table OID [" + info.getOid() + "] " + event.getErrorMessage());
                continue;
            }

            VariableBinding[] varBindings = event.getVariableBindings();
            if (varBindings == null || varBindings.length == 0) {
                continue;
            }
            int i=1;
            for (VariableBinding varBinding : varBindings) {
                if (varBinding == null) {
                    continue;
                }

                MibVO mib = new MibVO();
                mib.setId(i++);
                mib.setOid(varBinding.getOid().toString());
                String Syntax=CaseType(varBinding.getSyntax());
                mib.setType(Syntax);
                mib.setIp(info.getIp());
                mib.setValue(varBinding.getVariable().toString());
                list.add(mib);

            }

        }
        snmp.close();

        return list;
    }

//        public static List<MibVO> doWalk(SnmpMessageDTO info) throws IOException {
//
//
//        List<MibVO> list =new ArrayList<>();
//
//        //创建目标对象
//        CommunityTarget target =null;
//        target = createTarget(info);
//
//        //初始化Snmp
//        Snmp snmp=initSnmp(info);
//        PDU pdu=getPDU(info);
//
//
//        ResponseEvent revent=snmp.send(pdu, target);
//
//
//
//            if (revent != null && revent.getResponse() != null) {
//                Vector<? extends VariableBinding> recVBs = revent.getResponse()
//                        .getVariableBindings();
//                for (int i = 0; i < recVBs.size(); i++) {
//                    VariableBinding recVB = recVBs.elementAt(i);
//                    MibVO mib = new MibVO();
//                    mib.setId(i++);
//                    mib.setOid(recVB.getOid().toString());
//                    String Syntax=CaseType(recVB.getSyntax());
//                    mib.setType(Syntax);
//                    mib.setIp(info.getIp());
//                    mib.setValue(recVB.getVariable().toString());
//                    list.add(mib);
//
////                    System.out.println(recVB.getOid() + " : "
////                            + recVB.getVariable().toString());
//                }
//            }
//
////            int i=1;
////            for (VariableBinding varBinding : varBindings) {
////                if (varBinding == null) {
////                    continue;
////                }
////
////                MibVO mib = new MibVO();
////                mib.setId(i++);
////                mib.setOid(varBinding.getOid().toString());
////                String Syntax=CaseType(varBinding.getSyntax());
////                mib.setType(Syntax);
////                mib.setIp(info.getIp());
////                mib.setValue(varBinding.getVariable().toString());
////                list.add(mib);
////
////            }
//
//
//        snmp.close();
//
//        return list;
//    }

    public static List<MibVO> doSet(SnmpMessageDTO info) throws IOException {

        List<MibVO> list =new ArrayList<>();

        //创建目标对象
        CommunityTarget target =null;
        target = createTarget(info);

        //初始化Snmp
        Snmp snmp=initSnmp(info);
        PDU pdu=setPDU(info);
        ResponseEvent revent=snmp.send(pdu, target);

        if (revent != null && revent.getResponse() != null) {
            Vector<? extends VariableBinding> recVBs = revent.getResponse()
                    .getVariableBindings();
            for (int i = 0; i < recVBs.size(); i++) {
                VariableBinding recVB = recVBs.elementAt(i);
                MibVO mib = new MibVO();
                mib.setId(1);
                mib.setOid(recVB.getOid().toString());
                mib.setIp(info.getIp());
                list.add(mib);
            }
        }
        return list;
    }

    public static PDU setPDU(SnmpMessageDTO info) throws IOException {
        // PDU 对象
        PDU pdu = new PDU();
        //pdu.add(new VariableBinding(new OID(".1.3.6.1.2.1.1.5.0")));

        pdu.add(new VariableBinding(new OID(info.getOid()),new OctetString(info.getMessage())));
        // 操作类型
        pdu.setType(PDU.SET);
        //pdu.setType(PDU.GET);
        return pdu;
    }

    public static PDU getPDU(SnmpMessageDTO info) throws IOException {
        // PDU 对象
        PDU pdu = new PDU();
        pdu.add(new VariableBinding(new OID(info.getOid())));
        // 操作类型
        pdu.setType(PDU.GET);
        return pdu;
    }

    /**
     * 判断数据类型
     */
    public static String CaseType(int TypeNum) throws IOException {
        String Syntax = null;
        switch(TypeNum){
            case 6: Syntax="OID"; break;
            case 65:Syntax="Counter32"; break;
            case 70:Syntax="Counter64"; break;
            case 2:Syntax="Interger"; break;
            case 66:Syntax="Gauge32"; break;
            case 64:Syntax="IpAddress"; break;
            case 4:Syntax="OctetString"; break;
            case 67:Syntax="TimeTicks"; break;
            case 68:Syntax="Opaque"; break;
        }
        return Syntax;
    }

    /**
     * 创建报文
     * @param type
     * @return
     */
    private static PDU createPDU(SnmpMessageDTO info,int type){
        PDU pdu=null;
        if(info.getVersion()==SnmpConstants.version3){
            pdu= new ScopedPDU();
        }else {
            pdu= new PDUv1();
        }
        pdu.setType(type);
        //可以添加多个变量oid
        /*for(String oid:oids){
            pdu.add(new VariableBinding(new OID(oid)));
        }*/
        pdu.add(new VariableBinding(new OID(info.getOid())));
        return pdu;
    }




    /**
     * 创建v1/v2目标对象
     */
    public static CommunityTarget createTarget(SnmpMessageDTO info){
        String address = "udp:" + info.getIp() + "/161";
        CommunityTarget target;
        if(!(info.getVersion()==SnmpConstants.version1||info.getVersion()==SnmpConstants.version2c||info.getVersion()==SnmpConstants.version3)){
            return null;
        }
        target=new CommunityTarget();
        //snmpV1和snmpV2需要指定团体名名称
        target.setCommunity(new OctetString(info.getCommunity()));
        target.setVersion(info.getVersion());   //0-V1 1-V2 2-V3
        target.setAddress(GenericAddress.parse(address));
        target.setRetries(2);
        target.setTimeout(1500);
        return target;

    }

    /**
     * 创建v3目标对象
     */
    public static Target createTarget3(SnmpMessageDTO info){

        String address = "udp:" + info.getIp() + "/161";
        Target target;
        if(!(info.getVersion()==SnmpConstants.version1||info.getVersion()==SnmpConstants.version2c||info.getVersion()==SnmpConstants.version3)){
            return null;
        }
        target = new UserTarget();
        //snmpV3需要设置安全级别和安全名称，其中安全名称是创建snmp指定user设置的new OctetString("SNMPV3")
        target.setSecurityLevel(SecurityLevel.AUTH_PRIV);
        target.setSecurityName(new OctetString(info.getUsername()));

        target.setVersion(info.getVersion());
        target.setAddress(GenericAddress.parse(address));
        target.setRetries(2);
        target.setTimeout(1500);
        return target;

    }

    /**
     * 初始化snmp
     * @throws IOException
     */
    public static Snmp initSnmp(SnmpMessageDTO info) throws IOException {
        //1、初始化多线程消息转发类
        MessageDispatcher messageDispatcher = new MessageDispatcherImpl();
        //其中要增加三种处理模型。如果snmp初始化使用的是Snmp(TransportMapping<? extends Address> transportMapping) ,就不需要增加
        if(info.getVersion()==SnmpConstants.version1) {
            messageDispatcher.addMessageProcessingModel(new MPv1());
        }
        if(info.getVersion()==SnmpConstants.version2c) {
            messageDispatcher.addMessageProcessingModel(new MPv2c());
        }
        //当要支持snmpV3版本时，需要配置user
        if(info.getVersion()==SnmpConstants.version3) {
            OctetString localEngineID = new OctetString(MPv3.createLocalEngineID());
            USM usm = new USM(SecurityProtocols.getInstance().addDefaultProtocols(), localEngineID, 0);

            OctetString userName = new OctetString(info.getUsername());
            OctetString authpass = new OctetString(info.getAuthPassword());
            OctetString privpass = new OctetString(info.getPrivPassword());
            UsmUser user = new UsmUser(userName, AuthMD5.ID, authpass, PrivDES.ID, privpass);
            usm.addUser(user.getSecurityName(), user);
            messageDispatcher.addMessageProcessingModel(new MPv3(usm));
        }
        DefaultUdpTransportMapping transportMapping = new DefaultUdpTransportMapping();
        Snmp snmp;
        snmp = new Snmp(messageDispatcher,transportMapping);
        snmp.listen();
        return snmp;
    }

}
